﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Linq;

using ESRI.ArcGIS.Geodatabase;
using ESRI.ArcGIS.Geometry;
using ESRI.ArcGIS.SystemUI;
using ESRI.ArcGIS.Controls;
using System.Data;
using MyEngineExercise.我的扩展.其他;

namespace MyEngineExercise.我的扩展
{
    public static class MyAeExtendMethods
    {

        #region IFeatureClass扩展方法

        /// <summary>
        /// 判断一个FeatureClass的数据源是否Shapefile文件
        /// </summary>
        /// <param name="featureClass"></param>
        /// <returns></returns>
        public static Boolean IsShapeFile(this IFeatureClass featureClass)
        {
            //MyAeMethods.NewWorkspace(featureclass).Type == esriWorkspaceType.esriFileSystemWorkspace
            IDataset dataset = (IDataset)featureClass;
            return dataset.Workspace.IsDirectory() && dataset.Type == esriDatasetType.esriDTFeatureClass &&
                dataset.Category.IndexOf(Constants.SHAPE_FILE_CATEGORY, StringComparison.OrdinalIgnoreCase) >= 0;
        }



        /// <summary>
        /// 判断featureclass是否在mdb文件中
        /// </summary>
        /// <param name="featureClass"></param>
        /// <returns></returns>
        public static Boolean IsInMdbFile(this IFeatureClass featureClass)
        {
            IDataset dataset = (IDataset)featureClass;
            return !dataset.Workspace.IsDirectory() && dataset.Workspace.Type == esriWorkspaceType.esriLocalDatabaseWorkspace;

        }



        /// <summary>
        /// 判断FeatureClass有无记录
        /// </summary>
        /// <param name="featureClass"></param>
        /// <returns></returns>
        public static Boolean IsHasRecord(this IFeatureClass featureClass)
        {
            return featureClass.FeatureCount(null) > 0;
        }



        /// <summary>
        /// 判断是否是多边形
        /// </summary>
        /// <param name="featureClass"></param>
        /// <returns></returns>
        public static Boolean IsPolygon(this IFeatureClass featureClass)
        {
            return featureClass.ShapeType == esriGeometryType.esriGeometryPolygon;
        }


        /// <summary>
        /// 能否将Featureclass中的Field转换为datatable类型
        /// </summary>
        /// <param name="featureClass"></param>
        /// <returns></returns>
        public static Boolean CanFieldTypeToDataTableType(this IFeatureClass featureClass)
        {
            return featureClass.Fields.ToList().Any(v => Constants.CAN_CHANGE_FIELD_TYPES.Contains(v.Type));
        }


        /// <summary>
        /// 根据条件将FeatureClass中的多个多边形合并成一个，过滤不符合条件的要素（polygon）
        /// </summary>
        /// <param name="featureClass">传入的featureclass</param>
        /// <param name="whereColumn">过滤条件</param>
        /// <returns>返回一个新的多边形</returns>
        public static IPolygon UnionPolygons(this IFeatureClass featureClass, String whereColumn)
        {

            //Check input objects.
            if (featureClass == null)//如果为空
            {
                return null;
            }

            if (featureClass.FeatureCount(null) == 0)
                return null;//如果featureclass中没有要素

            IGeoDataset geoDataset = featureClass as IGeoDataset;

            //You can use a spatial filter to create a subset of features to union together, 
            //in order to do that uncomment the next line, set the properties of the spatial filter here.
            //Also change the first parameter in the IFeatureCursor::Seach method.
            //ISpatialFilter queryFilter = new SpatialFilterClass();

            IGeometry geometryBag = new GeometryBagClass();

            //Define the spatial reference of the bag before adding geometries to it.
            geometryBag.SpatialReference = geoDataset.SpatialReference;//设定空间参考

            //Use a nonrecycling cursor so each returned geometry is a separate object. 
            IQueryFilter filter = new QueryFilterClass();
            filter.WhereClause = whereColumn == String.Empty ? "1>0" : whereColumn;
            filter.SubFields = featureClass.ShapeFieldName;//shape字段,相当于select from。。。

            IFeatureCursor featureCursor = featureClass.Search(filter, false);

            IGeometryCollection geometryCollection = geometryBag as IGeometryCollection;
            IFeature currentFeature = featureCursor.NextFeature();

            while (currentFeature != null)//如果当前要素不为空
            {
                //Add a reference to this feature's geometry into the bag.
                //You don't specify the before or after geometry (missing),
                //so the currentFeature.Shape IGeometry is added to the end of the geometryCollection.
                object missing = Type.Missing;
                geometryCollection.AddGeometry(currentFeature.Shape, ref missing, ref  missing);

                currentFeature = featureCursor.NextFeature();
            }
            // Create the polygon that will be the union of the features returned from the search cursor.
            // The spatial reference of this feature does not need to be set ahead of time. The 
            // ConstructUnion method defines the constructed polygon's spatial reference to be the same as 
            // the input geometry bag.
            ITopologicalOperator unionedPolygon = new PolygonClass();
            //合并多边形
            unionedPolygon.ConstructUnion(geometryBag as IEnumGeometry);

            return unionedPolygon as IPolygon;
        }


        /// <summary>
        /// 获取FeatureClass的完整路径，没有后缀
        /// </summary>
        /// <param name="featureClass"></param>
        /// <returns></returns>
        public static String GetFullPath(this IFeatureClass featureClass)
        {
            IDataset dataset = (IDataset)featureClass;
            IFeatureDataset featureDataset = (IFeatureDataset)featureClass.FeatureDataset;
            
            String fullPath = String.Empty;
            //如果为空，合并pDataset.Workspace.PathName和pDataset.Name
            if (null == featureDataset)//即featureclass的数据源是shapefile 或 mdb数据源，但没有dataset
            {
                fullPath = System.IO.Path.Combine(dataset.Workspace.PathName, dataset.Name);
            }
            //如果不为空合并pDataset.Workspace.PathName和（pFeatureDataset.Name和pDataset.Name）
            else
            {
                fullPath = System.IO.Path.Combine(dataset.Workspace.PathName,System.IO.Path.Combine(featureDataset.Name,dataset.Name));
            }
            return fullPath;
        }


        /// <summary>
        /// 将featureClass的field转换为DataTable
        /// </summary>
        /// <param name="?"></param>
        /// <param name="fieldToDataTableMappingInfos"></param>
        /// <returns></returns>
        public static DataTable FieldToDataTable(this IFeatureClass featureClass, out List<FieldToDataTableInfo> outFieldToDataTableInfos)
        {
            //如果为空，返回为空
            if (featureClass.CanFieldTypeToDataTableType() == false)
            {
                outFieldToDataTableInfos = null;
                return null;
            }

            //将field转换为DataTable
            List<FieldToDataTableInfo> fieldToDataTableInfos = featureClass.Fields.GetFieldToDataTableInfos();

            //如果没有转换信息
            if (fieldToDataTableInfos.Count == 0)
            {
                outFieldToDataTableInfos = null;
                return null;
            }

            // 构建表(DataTable)
            DataTable dataTable = new DataTable(featureClass.AliasName);

            #region 设计dataTable行
            //设置表的每一个字段
            foreach (FieldToDataTableInfo fieldToDataTableInfo in fieldToDataTableInfos)
            {
                //待转换的字段
                IField field = featureClass.Fields.get_Field(fieldToDataTableInfo.FieldIndex);

                //DataTable的列
                DataColumn dataTableColumn = dataTable.Columns.Add(fieldToDataTableInfo.FieldName, fieldToDataTableInfo.DataTableColumnType);
                dataTableColumn.SetOrdinal(fieldToDataTableInfo.DataTableIndex);//将 System.Data.DataColumn 的序号或位置更改为指定的序号或位置
                dataTableColumn.ReadOnly = true;
                dataTableColumn.Caption = fieldToDataTableInfo.FieldName;//设置标题
                dataTableColumn.AllowDBNull = field.IsNullable;//是否允许为空
                dataTableColumn.DefaultValue = field.DefaultValue;//默认值

                if (fieldToDataTableInfo.FieldType == esriFieldType.esriFieldTypeOID)//如果是objectID字段
                    dataTableColumn.Unique = true;//设置为唯一
                if (fieldToDataTableInfo.FieldType == esriFieldType.esriFieldTypeString)//如果是String字段
                    dataTableColumn.MaxLength = featureClass.Fields.get_Field(fieldToDataTableInfo.FieldIndex).Length;

            }

            //为DataTable添加主键,any任何一个满足就返回true，.NET的注释错误
            if (fieldToDataTableInfos.Any(v => v.FieldType == esriFieldType.esriFieldTypeOID))
            {
                FieldToDataTableInfo keyInfo = fieldToDataTableInfos.First(v => v.FieldType == esriFieldType.esriFieldTypeOID);
                dataTable.PrimaryKey = new DataColumn[] { dataTable.Columns[keyInfo.DataTableIndex],};
            }

            // 看看没有有面积字段，没有则往DataTable中添加面积列
            Boolean isNeedToAddAreaField= featureClass.IsPolygon() && null == featureClass.AreaField;
            
            if (isNeedToAddAreaField)
            {
                int ordinal = fieldToDataTableInfos.Select(v => v.DataTableIndex).Max() + 1;

                DataColumn areaDataColumn = dataTable.Columns.Add("面积（平方米）（打开属性表时添加的字段）", typeof(Double));
                areaDataColumn.AllowDBNull = true;
                areaDataColumn.DefaultValue = 0;
                areaDataColumn.ReadOnly = true;
                areaDataColumn.SetOrdinal(ordinal);

                fieldToDataTableInfos.Add(new FieldToDataTableInfo(areaDataColumn.ColumnName, areaDataColumn.DataType, areaDataColumn.Ordinal));
            }

            #endregion

            #region 遍历featureClass中的记录，往dataTable中添加数据

            //用featureCursor读取featureclass中的值，然后将数据写入DataTable
            IFeatureCursor featureCursor = featureClass.Search(null, false);//false，传值

            IFeature feature = null;

            //找出不面积列的索引
            var fieldIndexs = fieldToDataTableInfos.Where(v => v.FieldIndex != -1).Select(v => v.FieldIndex);


            //遍历featureClass每一条非空记录
            while ((feature = featureCursor.NextFeature()) != null)
            {
                Double area = 0;//保存面积

                //获取改行其他列（即不是面积列）的值
                var fieldValues = fieldIndexs.Select(v => feature.get_Value(v));

                if (isNeedToAddAreaField)
                {
                    try
                    {
                        area = ((IArea)feature.Shape).Area;//几何面积
                    }
                    catch { }

                    //获取改行其他列（即不是面积列）的值
                    var values = fieldValues.ToList();

                    values.Add(area);//添加面积到集合中

                    dataTable.Rows.Add(values.ToArray());//往dataTable中添加数据
                }
                else
                    dataTable.Rows.Add(fieldValues.ToArray());
            }
            #endregion

            outFieldToDataTableInfos = fieldToDataTableInfos;
            return dataTable;
        }
        
        #endregion


        #region IFields扩展方法

        /// <summary>
        /// 转换为列表类型
        /// </summary>
        /// <param name="fields"></param>
        /// <returns></returns>
        public static List<IField> ToList(this IFields fields)
        {
            List<IField> fieldList = new List<IField>();

            for (int i = 0; i < fields.FieldCount; i++)
            {
                IField field = fields.get_Field(i);
                fieldList.Add(field);
            }

            return fieldList;
        }


        /// <summary>
        /// field转换为DataTable时，获取转换的信息
        /// </summary>
        /// <param name="fields"></param>
        /// <returns></returns>
        public static List<FieldToDataTableInfo> GetFieldToDataTableInfos(this IFields fields)
        {
            // 当前可以转化为DataColumn的esriFieldType类型

            int j = 0;//转换为dataTable后的索引
            List<FieldToDataTableInfo> fieldToDataTableInfos = new List<FieldToDataTableInfo>();

            //i是未转换之前的索引，即fields中的索引
            for (int i = 0; i < fields.FieldCount; i++)
            {
                IField field = fields.get_Field(i);

                if (Constants.CAN_CHANGE_FIELD_TYPES.Contains(field.Type) == false)
                    continue;
                else
                {
                    fieldToDataTableInfos.Add(new FieldToDataTableInfo(field.Name, field.Type, j, i));
                    j++;
                }
            }

            return fieldToDataTableInfos;
        }
        #endregion


        #region 工具栏扩展方法

        /// <summary>
        /// 根据工具的类型(AxToolbarControl扩展方法)，查找工具栏上指定的工具
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="axToolbarControl"></param>
        /// <returns>返回该工具</returns>
        public static ITool FindTool<T>(this AxToolbarControl axToolbarControl)
        {
            //将AxToolbarControl转换为IToolbarControl
            //根据类型找到工具后，转换为ITool
            IToolbarControl pToolbarControl = (IToolbarControl)axToolbarControl.Object;
            //ITool tool = (ITool)pToolbarControl.FindTool<T>();
            ITool tool = pToolbarControl.FindTool<T>();
            return tool;
        }


        /// <summary>
        /// 根据工具的类型(IToolbarControl扩展方法)，查找工具栏上指定的工具
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="pToolbarControl"></param>
        /// <returns>返回该工具的类型</returns>
        public static ITool FindTool<T>(this IToolbarControl pToolbarControl)
        //public static T FindTool<T>(this IToolbarControl pToolbarControl)
        {
            /*//遍历toolbar上的item
            for (int i = 0; i < pToolbarControl.Count; i++)
            {
                //第i个item
                IToolbarItem2 item = (IToolbarItem2)pToolbarControl.GetItem(i);
                if (item.ItemObject is T)//如果item的类型是T
                    return (T)item.ItemObject;//返回类型T
            }
            return default(T);//返回默认的T类型*/
            //遍历toolbar上的item
            for (int i = 0; i < pToolbarControl.Count; i++)
            {
                //第i个item
                IToolbarItem2 item = (IToolbarItem2)pToolbarControl.GetItem(i);
                if (item.ItemObject is T)//如果item的类型是T
                    return (ITool)item.ItemObject;//返回类型T
            }
            return null;
        }

        #endregion


        #region polygon

        /// <summary>
        /// 获取多边形的点数.界址点数
        /// </summary>
        /// <param name="polygon"></param>
        /// <returns></returns>
        public static Int32 PointCount(this IPolygon polygon)
        {
            Int32 pointCount = 0;//点计数器
            String resultInfo;

            if (polygon.CanUse(out resultInfo) == false)
                return pointCount;

            IPolygon4 polygon4 = (IPolygon4)polygon;//转换为IPolygon4

            IGeometryBag exteriorRingBag = polygon4.ExteriorRingBag;//外环bag
            IGeometryCollection exteriorRingGeometryCollection = (IGeometryCollection)exteriorRingBag;

            //遍历多边形中的每个外环
            for (int i = 0; i < exteriorRingGeometryCollection.GeometryCount; i++)
            {
                IGeometry exteriorRingGeometry = exteriorRingGeometryCollection.get_Geometry(i);//第i个外环

                IPointCollection exteriorRingPointCollection = (IPointCollection)exteriorRingGeometry;//获取第i个外环的所有点的集合

                pointCount += exteriorRingPointCollection.PointCount;//累加点数



                IGeometryBag interiorRingBag = polygon4.get_InteriorRingBag((IRing)exteriorRingGeometry);//第i个外环的内环bag

                IGeometryCollection interiorRingGeometryCollection = (IGeometryCollection)interiorRingBag;

                //遍历第i个外环里面所有的内环
                for (int j = 0; j < interiorRingGeometryCollection.GeometryCount; j++)
                {
                    IGeometry interiorRingGeometry = interiorRingGeometryCollection.get_Geometry(j);//第j个内环

                    IPointCollection interiorRingPointCollection = (IPointCollection)interiorRingGeometry;//获取第j个内环的所有点的集合

                    pointCount += interiorRingPointCollection.PointCount;//累加点数
                }
            }

            return pointCount;
        }


        /// <summary>
        /// 判断一个多边形是否可用(是否已闭合、拓扑正确,并检查polygon是否有外环)
        /// </summary>
        /// <param name="polygon"></param>
        /// <param name="?"></param>
        /// <returns></returns>
        public static Boolean CanUse(this IPolygon polygon,out String resultInfo)
        {
            if (null == polygon)
            {
                resultInfo = Constants.IPOLYGON_NULL;
                return false;
            }
            if (polygon.IsEmpty)
            {
                resultInfo = Constants.IPOLYGON_EMPTY;
                return false;
            }
            if (polygon.IsClosed == false)
            {
                resultInfo = Constants.IPOLYGON_IS_NOT_CLOSE;
                return false;
            }

            try
            {
                ITopologicalOperator2 topologicalOperator = (ITopologicalOperator2)polygon;
                topologicalOperator.IsKnownSimple_2 = false;//首先假设为拓扑错误

                if (topologicalOperator.IsSimple == false)
                {
                    resultInfo = Constants.IPOLYGON_TOPOLOGICAL_ERROR;
                    return false;
                }

                //要放在拓扑检查后面?
                IPolygon4 polygon4 = (IPolygon4)polygon;

                if (null == polygon4)
                {
                    resultInfo = Constants.IPOLYGON4_NULL;
                    return false;
                }

                if (polygon4.ExteriorRingCount == 0)//如果多边形的外环数为0
                {
                    resultInfo = Constants.IPOLYGON4_EXTERIOR_RING_COUNT_ZERO;
                    return false;
                }

                //要再检查一次？
                topologicalOperator = (ITopologicalOperator2)polygon4;
                topologicalOperator.IsKnownSimple_2 = false;

                if (topologicalOperator.IsSimple == false)
                {
                    resultInfo = Constants.IPOLYGON4_TOPOLOGICAL_ERROR;
                    return false;
                }

                resultInfo = Constants.NO_ERROR;
                return true;
            }
            catch
            {
                resultInfo = Constants.CONVERT_ERROR;
                return false;
            }
        }

        /// <summary>
        /// (新)判断一个多边形是否可用(是否已闭合、拓扑正确,并检查polygon是否有外环)
        /// </summary>
        /// <param name="polygon">传入的多边形</param>
        /// <returns>可用则返回true,否则返回false</returns>
        public static Boolean CanUse2(this IPolygon polygon, out Int32 result)
        {
            //为空
            if (polygon == null) { result = Constants.EC11900; return false; }
            if (polygon.IsEmpty) { result = Constants.EC11901; return false; }
            //没有闭合
            if (!polygon.IsClosed) { result = Constants.EC11902; return false; }
            try
            {
                var topoOper = (ITopologicalOperator2)polygon;
                /**如果两名必须放在将 polygon 转换为 esriGeometry.IPolygon4 之前
                 * 因为 polygon 有可能处理Editing 状态,而没有结束，这时执行转换
                 * 一定是错误的，而调用  IsSimple 时有可能会执行一个 Simplify  方法
                 * 英文的解释
                 * IsKnownSimple_2
                 * Indicates whether this geometry is known (or assumed) to be topologically correct. {set;}
                 * 
                 * IsSimple 
                 * Indicates whether this geometry is known (or assumed) to be topologically correct, 
                 * after explicitly determining this if the geometry is not already known (or assumed) to be simple. 
                 * 
                 * Returns TRUE if the geometry is topologically Simple (refer to the discussion for the Simplify method).  
                 * If the geometry is not Simple, it may be necessary to call Simplify to enforce topological consistency. 
                 * Editing a geometry can change the IsSimple state of the geometry.
                 */
                //首先假设为拓扑错误
                topoOper.IsKnownSimple_2 = false; // 先设置为 false,确保 IsSimple 返回正确的结果
                var b = topoOper.IsSimple;
                if (!b) //如果b=false，拓扑错误
                {
                    result = Constants.EC11903; return false;
                }

                var poly4 = (IPolygon4)polygon;
                //如果为空
                if (poly4 == null) { result = Constants.EC11904; return false; }

                //如果多边形的外部ring数为0
                if (polygon.ExteriorRingCount == 0) { result = Constants.EC11905; return false; }

                //再检查拓扑，检查2次？
                topoOper.IsKnownSimple_2 = false;
                if (topoOper.IsSimple)//拓扑正确
                {
                    Console.ForegroundColor = ConsoleColor.Green;//设置输出的颜色
                    Console.WriteLine("CanUse2:: 这个多边形可用！");
                    result = 0;
                    return true;
                }
                else//拓扑错误
                {
                    Console.WriteLine("CanUse2:: topoOper.IsSimple = false");
                    result = Constants.EC11906;
                    return false;
                }
            }
            catch
            {
                Console.WriteLine("CanUse2:: 强转时发生错误");
                result = Constants.EC11907;
                return false;
            }
            finally
            {
                Console.ResetColor();
            }
        }

        #endregion
    }
}
